<body>
<div id="setting" class="row">
	<div id="application_generator" class="col-xs-6 col-md-6">
		<form id="generates">
			<div class="input-group">
			</div>
		</form>
	</div>
	<div id="NoCBuilder" class="col-xs-6 col-md-6">
		<form id="builds">
			<div class="input-group">
			</div>
		</form>
	</div>
</div>

<div id="running" class="row">
	<div id="applications" class="col-md-3">
		<button id="start_simulation">Simulate!</button>
		<div id="applications_display">
			
		</div>
	</div>
	<div id="NoC" class="col-md-6"></div>
	<div id="evaluation matrix" class="col-md-3">
		<div id="real_time" class="thumbnail">
		</div>
		
		<div id="finished" class="thumbnail">
		</div>
	</div>
	</div>
</div>
</body>

var applicationConfig = {
    	applicationNumber : parseInt(document.forms["generates"].elements['applicationNumber'].value),
    	taskNumber : parseInt(document.forms["generates"].elements['taskNumber'].value),
    	taskNumberVarient : parseInt(document.forms["generates"].elements['taskNumberVarient'].value),
    	executionVolume : parseFloat(document.forms["generates"].elements['executionVolume'].value),
    	executionVolumeVarient : parseFloat(document.forms["generates"].elements['executionVolumeVarient'].value),
    	CCR : parseFloat(document.forms["generates"].elements['CCR'].value),
    	CCRVarient : parseFloat(document.forms["generates"].elements['CCRVarient'].value),
    	communicationVolume : parseFloat(document.forms["generates"].elements['communicationVolume'].value),
    	communicationVolumeVarient : parseFloat(document.forms["generates"].elements['communicationVolumeVarient'].value),
    	arrivalRate : parseFloat(document.forms["generates"].elements['arrivalRate'].value),
    	arrivalRateVarient : parseFloat(document.forms["generates"].elements['arrivalRateVarient'].value),
    	type : document.forms["generates"].elements['type'].value
    }

function applicationGenerator(applicationConfig){
	var apps = [];
	for (var appIndex = 0;appIndex<applicationConfig.applicationNumber;appIndex++ ){
		var app = {
			id:appIndex,
			taskNum:applicationConfig.taskNumber,
			arrivalTime:Math.ceil(appIndex/applicationConfig.arrivalRate),
			tasks:[]
		}
		for (var taskIndex = 0;taskIndex<applicationConfig.taskNumber;taskIndex++ ){
			var task = {
					id:taskIndex,
					execution:applicationConfig.executionVolume,
					neibors:[]
				}
			switch(applicationConfig.type){
				case "star":
					buildStar(appIndex)
					break;
				case "line":
					buildLine(appIndex);
					break;
				default:
				break;
			}
			app["tasks"].push(task);
		}
		apps.push(app);
	}

	return apps;
}
var application = {
				id:0,
				taskNum:2,
				arrivalTime:0,
				tasks:[{id:0,
						execution:128,
						neibors:[0,4]},
						{id:1,
						execution:128,
						neibors:[-4,0]}]
			}

var taskNumMax = 0;
for(each in applications){
	if(applications[each].taskNum > taskNumMax){
		taskNumMax = applications[each].taskNum;
	}
}

var applicationDisplayDiv = document.getElementById("applications_display")
//生成一个Two对象，注意绘图区域大小随着应用数和任务数变化
var two = new Two({width:taskNumMax*20,height:applications.length*taskNumMax*20}).appendTo(applicationDisplayDiv);
var center = {x:taskNumMax*10,y:0};
//依次绘制应用集合内的每个应用
for(var index in applications){
	center.y += applications[index].taskNum*10;
	drawApplicaton(applications[index],center,two);
	center.y += applications[index].taskNum*10;
}
//将two对象渲染到界面上
two.update();	

//在Two对象上添加某个应用
function drawApplicaton(application,center,two){
	for(eachTask in application.tasks){

	}
}

var applications=[];
var NoCConfig;
var status;

app.post('/index',function(req,res){

	res.end();
});

app.post('/generates',function(req,res){
	applications = req.body;
	var applicationFilePrefix = "..\\simulate\\app\\app";
    for (applicationIndex in applications) {
        writeIntoFile(applications[applicationIndex],applicationFilePrefix)
	}
	res.end("generated");
});

app.post('/builds',function(req,res){
	//console.log(JSON.parse(req.body));
	NoCConfig = req.body;
	res.end("built");
});

var WebSocketServer = require('ws').Server, wss = new WebSocketServer({port: 8080});
app.post('/simulates',function(req,res){
	var simulate = spawn("..\\simulate\\Simulator\\x64\\Debug\\Simulator.exe",[NoCConfig.width+"",applications.length+""]);
    wss.on('connection', function (ws) {
        console.log("ws connected!");
        readline.createInterface({
            input     : simulate.stdout,
            terminal  : false
        }).on('line', function (line) {
            
            var message = {
                    taskProfile: line
                }
            console.log(message.taskProfile);
            ws.send(JSON.stringify(message));
        });
    });
	res.end();
})

class Event {
private:
	vector<InvokedTask> tasks;				//子任务对象数组
	vector<vector<double>> taskGraph;		//子任务间通信状态
	int arrivalTime;						//到达时间
	int numTasks;							//子任务数
	int id;									//事件编号
	
	State state;							//事件当前状态
	int startTime;							//事件开始执行时间
	int finishTime;							//事件结束执行时间
	
public:
	Event(Application a);
	bool arrive(int now);						//判断事件是否到达
	void updateTaskGraph();
	void showTaskGraph();

	bool startRunning(Map* map,int type);		//判断是否可以开始运行
	bool mapping(Map* map,int type);			//任务映射

	bool settleDown(Map* map,int type);
	double communicationCost(Map map);
	void finish(Map* map);
	void unhookFromMap(Map* map);				//释放已经映射的核
	int calculateRemainingTime(Map* map);		//计算剩余时间

	void nRound(Map* map, int n = 1);			//执行n个时钟周期
	bool hasFinished();							//判断事件是否结束
	void wait();
}

class Task {
private:
	int executionVolume;
	vector<double> withNeibors;// >0, parent;
	
	int id;
	int applicationId;

public:
	Task(int ev, int aId, int tid);
	int getExecutionVolume();
	int getApplicationId();
	int getId();
	vector<double> getWithNeibors();

	void setWithNeibors(vector<double> mask);
	void bondingwith(int another,double dataSize);// type 0,child; type 1, parent;
};

class Application {
private:
	vector<Task> tasks;
	vector<vector<double>> taskGraph;
	int numTasks;
	int arrivalTime;
	int id;
	double CCR;
	void buildTaskGraph();
public:
	Application(int nT = 0,double r = 1.0, int aT = 0, int i = 0,int taskVol = 0);
	Application(string filename);
	int getArrivalTime();
	int getNumTasks();
	vector<Task> getTasks();
	int getId();
	vector<vector<double>> getTaskGraph();
	void saveAsFile(string filenamePrefix);
};


vector<Event> events = readEventFromFile(numApplication,filenamePrefix)
vector<Event> waiting;
vector<Event> running;
vector<Event> finished;
double commCost = 0;

Clock* clock = Clock::getClock();
while (finished.size() != numApplication) {
	for (event in waiting) {
		if (event.startRunning(&map,type)) {
			commCost += event.communicationCost(map);
			running.push_back(event);
			waiting.erase(event);
		}
	}

	while (!events.empty()) {
		Event event = events.front();
		if (event.arrive(clock->now())) {
			if (event.startRunning(&map,type)) {
				running.push_back(app);
				commCost += event.communicationCost(map);
				events.pop();
			}
			else {
				event.wait();
				waiting.push_back(event);
				events.pop();
			}
		}
		else {
			break;
		}

	}

	for (event in running) {
		event.nRound(&map,1);
		if (event.hasFinished()) {
			finished.push_back(event);
			event = running.erase(event);
		}
	}
	map.showMap();
	map.savePower(powerPrefix);
	clock->tick();
}


class Map {
private:
	vector<Core> cores;
	int width;
	int height;
	int length;
	int availableCores;
public:
	Map(int length = 1, int height = 1,int width = 1,double es = 10,double is = 1,double os = 1, int fraction = 0,string powerPrefix="");
	int getWidth();
	int getHeight();
	int getLength();
	bool turnOnCore(InvokedTask task,Vertex ver);
	bool turnOffCore(Vertex ver);
	int getAvailableCores();
	Core* getSpecificCore(Vertex v);
	Core* getSpecificCore(int index);
	int toIndex(Vertex v);
	Vertex toVertex(int i);
	void showMap();
	void savePower(string filePrefix="");
};

class Core {
private:
	Vertex coordinate;
	double inputSpeed;
	double outputSpeed;
	double executionSpeed;
	int applicationId;
	int taskId;
	static const int speedLevel[3];
public:
	/*core needs position and speeds*/
	Core(int l, int h,int w, double es, double is,double os);

	/*turn on the core running specific task*/
	void turnOn(int aId, int tId);

	/*turn off the core*/
	void turnOff();

	/*returns true if the core is busy,false otherwise*/
	bool checkState();

	Vertex getCoordinate();
	int getApplicationId();
	double getExecutionSpeed();
	double getInputSpeed();
	double getOutputSpeed();
};


struct Vertex {
	int h;
	int w;
	int l;
	Vertex(int length = -1, int height = -1,int width = -1);
	int toIndex(int length, int height, int width);
	Vertex operator-(Vertex v);
	bool operator==(Vertex v);
	int Manhattan(Vertex v);

	//static Vertex toVertex(int i, Map map);
};

Map::Map(int l, int h,int w,double es,double is,double os) {
	width = w;
	height = h;
	length = l;
	for (int l = 0; l < length; l++) {
		for (int h = 0; h < height; h++) {
			for (int w = 0; w < width; w++) {
				Core core(h, w,l,es,is,os);
				cores.push_back(core);
			}
		}
	}
	availableCores = height * width*length;
}

queue<Event> readEventFromFile(int numApplication, string filenamePrefix) {
	queue<Event> results;
	for (int i = 0; i < numApplication; i++) {
		Application app(filenamePrefix + to_string(i) + ".txt");
		Event e(app);
		results.push(e);
	}
	return results;
}

int width = atoi(argv[1]);
int height = atoi(argv[2]);
int length = atoi(argv[3]);
int numApplication = atoi(argv[4]);